// function assert(actual, expected, message) {
//     if (arguments.length == 1)
//         expected = true;

//     if (actual === expected)
//         return;

//     // if (actual !== null && expected !== null
//     // &&  typeof actual == 'object' && typeof expected == 'object'
//     // &&  actual.toString() === expected.toString())
//     //     return;

//     // throw Error("assertion failed: got |" + actual + "|" +
//     //             ", expected |" + expected + "|" +
//     //             (message ? " (" + message + ")" : ""));
// }


/*----------------*/
print('test -- closure.js')
var log_str = "";

function log(str)
{
    log_str += str + ",";
}

function f(a, b, c)
{

    var x = "10";
    log("a=" + a);
    function g(d) {
        print(d)
        function h() {
            print(d)
            log("d=" + d);
            log("x=" + x);
        }
        log("b=" + b);
        log("c=" + c);
        h();
    }
    g("4");
    return g;
}

var g1 = f("1", "2", "3");
g1("5");
print(log_str)

assert(log_str === "a=1,b=2,c=3,d=4,x=10,b=2,c=3,d=5,x=10,");

function test_closure1()
{
    function f2()
    {
        var val = 1;
        
        function set(a) {
            val = a;
        }
        function get(a) {
            return val;
        }
        return { "set": set, "get": get };
    }
    
    var obj = f2();
    obj.set(10);
    var r;
    r = obj.get(11);
    assert(r === 10);
}

function test_closure2()
{
    var expr_func = function myfunc1(n) {
        function myfunc2(n) {
            return myfunc1(n - 1);
        }
        if (n == 0)
            return 0;
        else
            return myfunc2(n);
    };
    var r;
    r = expr_func(1);
    assert(r === 0);
}

function test_closure3()
{
    function fib(n)
    {
        if (n <= 0)
            return 0;
        else if (n == 1)
            return 1;
        else
            return fib(n - 1) + fib(n - 2);
    }

    var fib_func = function fib1(n)
    {
        
        if (n <= 0)
            return 0;
        else if (n == 1)
            return 1;
        else
            return fib1(n - 1) + fib1(n - 2);
    };
    assert(fib(6) === 8);
    assert(fib_func(6) === 8);
}

// // function test_arrow_function()
// // {
// //     "use strict";

// //     function f1() {
// //         return (() => arguments)();
// //     }
// //     function f2() {
// //         return (() => this)();
// //     }
// //     function f3() {
// //         return (() => eval("this"))();
// //     }
// //     function f4() {
// //         return (() => eval("new.target"))();
// //     }
// //     var a;

// //     a = f1(1, 2);
// //     assert(a.length, 2);
// //     assert(a[0] === 1 && a[1] === 2);

// //     assert(f2.call("this_val") === "this_val");
// //     assert(f3.call("this_val") === "this_val");
// //     assert(new f4() === f4);

// //     var o1 = { f() { return this; } };
// //     var o2 = { f() {
// //         return (() => eval("super.f()"))();
// //     } };
// //     o2.__proto__ = o1;

// //     assert(o2.f() === o2);
// // }

// function test_with()
// {
//     var o1 = { x: "o1", y: "o1" };
//     var x = "local";
//     eval('var z="var_obj";');
//     assert(z === "var_obj");
//     with (o1) {
//         assert(x === "o1");
//         assert(eval("x") === "o1");
//         var f = function () {
//             o2 = { x: "o2" };
//             with (o2) {
//                 assert(x === "o2");
//                 assert(y === "o1");
//                 assert(z === "var_obj");
//                 assert(eval("x") === "o2");
//                 assert(eval("y") === "o1");
//                 assert(eval("z") === "var_obj");
//                 assert(eval('eval("x")') === "o2");
//             }
//         };
//         f();
//     }
// }

// function test_eval_closure()
// {
//     var tab;

//     tab = [];
//     for(var i = 0; i < 3; i++) {
//         eval("tab.push(function g1() { return i; })");
//     }
//     for(var i = 0; i < 3; i++) {
//         assert(tab[i]() === i);
//     }

//     tab = [];
//     for(var i = 0; i < 3; i++) {
//         var f = function f() {
//             eval("tab.push(function g2() { return i; })");
//         };
//         f();
//     }
//     for(var i = 0; i < 3; i++) {
//         assert(tab[i]() === i);
//     }
// }

// function test_eval_const()
// {
//     const a = 1;
//     var success = false;
//     var f = function () {
//         eval("a = 1");
//     };
//     try {
//         f();
//     } catch(e) {
//         success = true;
//     }
//     assert(success);
// }

test_closure1();
test_closure2();
test_closure3();
// // test_arrow_function();
// // test_with();
// // test_eval_closure();
// // test_eval_const();
